/*
 * Copyright (C) 2006-2007 Sun Microsystems, Inc. All rights reserved. Use is
 * subject to license terms.
 */

package org.gwt.beansbinding.core.client;

import java.util.ArrayList;
import java.util.StringTokenizer;

/**
 * 
 * @author Shannon Hickey
 * @author Scott Violet
 */
abstract class PropertyPath {

  private PropertyPath() {
    // Nothing to do here!
  }

  public abstract int length();

  public abstract String get(int index);

  public String getLast() {
    return get(length() - 1);
  }

  public abstract String toString();

  public static PropertyPath createPropertyPath(String path) {
    if (path == null) {
      throw new IllegalArgumentException("path must be non-null");
    }

    StringTokenizer tokenizer = new StringTokenizer(path, ".");
    ArrayList<String> list = new ArrayList<String>();
    while (tokenizer.hasMoreElements()) {
      list.add(tokenizer.nextToken());
    }

    int size = list.size();

    if (size == 0) {
      throw new IllegalArgumentException("path must be non-empty");
    } else if (list.size() == 1) {
      return new SinglePropertyPath(list.get(0));
    } else {
      String[] multi = new String[list.size()];
      return new MultiPropertyPath(list.toArray(multi));
    }
  }

  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }

    if (o instanceof PropertyPath) {
      PropertyPath oPath = (PropertyPath) o;

      int length = length();

      if (length != oPath.length()) {
        return false;
      }

      for (int i = 0; i < length; i++) {
        if (!get(i).equals(oPath.get(i))) {
          return false;
        }

        return true;
      }
    }

    return false;
  }

  public int hashCode() {
    int result = 17;
    int length = length();

    for (int i = 0; i < length; i++) {
      result = 37 * result + get(i).hashCode();
    }

    return result;
  }

  static final class MultiPropertyPath extends PropertyPath {
    private final String[] path;

    public MultiPropertyPath(String[] path) {
      this.path = path;

      for (int i = 0; i < path.length; i++) {
        path[i] = path[i].intern();
      }

      assert (path.length > 0);
    }

    public int length() {
      return path.length;
    }

    public String get(int index) {
      return path[index];
    }

    public String toString() {
      StringBuilder builder = new StringBuilder();
      builder.append(path[0]);
      for (int i = 1; i < path.length; i++) {
        builder.append('.');
        builder.append(path[i]);
      }
      return builder.toString();
    }
  }

  static final class SinglePropertyPath extends PropertyPath {
    private final String path;

    public SinglePropertyPath(String path) {
      this.path = path.intern();
    }

    public int length() {
      return 1;
    }

    public String get(int index) {
      if (index == 0) {
        return path;
      }

      throw new ArrayIndexOutOfBoundsException();
    }

    public String getLast() {
      return path;
    }

    public String toString() {
      return path;
    }
  }

}
